#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/netlink.h>

#define NETLINK_USER 31
#define MAX_PAYLOAD 1024  /* 最大负载 */

/*
 * 发送一条消息，然后等待内核回复。
 */
int main()
{
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    int sock_fd;
    struct msghdr msg;
    char *message = "Hello from user space";

    /* 创建 Netlink socket */
    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sock_fd < 0) {
        perror("socket");
        return -1;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();  /* 绑定到当前进程 PID */
    src_addr.nl_groups = 0;      /* 不加入任何多播组 */

    /* 绑定 socket */
    if (bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
        perror("bind");
        close(sock_fd);
        return -1;
    }

    /* 设置目的地址：内核 */
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;   /* 内核侧（pid=0） */
    dest_addr.nl_groups = 0;/* 单播 */

    /* 为要发送的消息申请内存并填充 netlink 头 */
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();  /* 发送者 PID */
    nlh->nlmsg_flags = 0;
    /* 将实际要发送的字符串复制到 netlink 消息的 payload 区域 */
    strcpy(NLMSG_DATA(nlh), message);

    /* 使用 iovec 封装要发送的数据 */
    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;

    /* 封装 msghdr */
    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (void *)&dest_addr;    /* 目的地址 */
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;                   /* 要发送的数据 */
    msg.msg_iovlen = 1;

    /* 发送消息给内核 */
    printf("Sending message to kernel: %s\n", (char *)NLMSG_DATA(nlh));
    if (sendmsg(sock_fd, &msg, 0) < 0) {
        perror("sendmsg");
        free(nlh);
        close(sock_fd);
        return -1;
    }

    /* 准备接收内核的回复 */
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    if (recvmsg(sock_fd, &msg, 0) < 0) {
        perror("recvmsg");
        free(nlh);
        close(sock_fd);
        return -1;
    }

    printf("Received message from kernel: %s\n", (char *)NLMSG_DATA(nlh));

    /* 释放资源 */
    free(nlh);
    close(sock_fd);
    return 0;
}
